/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: ServerSocketLogCollector.java 501 2007-05-04 15:06:32Z jacek.kolezynski $
 */
package logmatcher.collectors;

import logmatcher.Collector;
import logmatcher.Configurable;
import logmatcher.InitializationException;
import logmatcher.InputFilter;
import logmatcher.LogEventListener;
import logmatcher.LogEventProducer;
import logmatcher.Utils;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;


/**
 * Class represents server waiting for incoming connections. For each established connection another collector is
 * started, reading messages from stream.
 *
 * @version $Revision: 501 $
*/
public class ServerSocketLogCollector implements Collector, Configurable, Runnable, LogEventProducer {
    //~ Static fields/initializers -------------------------------------------------------------------------------------

    /** Class logger. */
    private static final Log LOGGER = LogFactory.getLog(ServerSocketLogCollector.class);

    //~ Instance fields ------------------------------------------------------------------------------------------------


    /** Working thread, carrying out listening to new connections. */
    private Thread worker = new Thread(this);

    /** TCP port number. */
    private int portNumber;
    
    private Socket socket1;
    private Socket socket2;
    
    private String hostName;

    /** Indicates if collector should be stopped as soon as possible. */
    private boolean stopped = true;

    /** Reference to configuration, used co configure other collectors. */
    private Configuration config;
    
    /** Destination of message. */
    private LogEventListener logEventListener;

    //~ Constructors ---------------------------------------------------------------------------------------------------

/**
     * Creates a new ServerSocketLogCollector object.
     * 
     * @throws IOException in case any server initialization error occurs.
     */
    public ServerSocketLogCollector() {
        super();
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    /**
     * @see Runnable#run()
     */
    public void run() {
            try {
            	socket1 = new Socket(hostName, portNumber);
            	if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Got socket 1");
                }
                CollectorSkeleton collector1 = new ObjectInputStreamLogCollector(socket1.getInputStream());
                collector1.addLogEventListener(logEventListener);
                collector1.start();                
            } catch (SocketException e) {
                LOGGER.info("Socket1 closed");

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Socket1 suspected to be closed", e);
                }
            } catch (IOException e) {
                LOGGER.error("Error while establishing a connection1", e);
            }
            try {
            	socket2 = new Socket(hostName, portNumber+1);
            	if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Got socket 2");
                }
                CollectorSkeleton collector2 = new ObjectInputStreamLogCollector(socket2.getInputStream());
                collector2.addLogEventListener(logEventListener);                
                collector2.start();
            } catch (SocketException e) {
                LOGGER.info("Socket2 closed");

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Socket2 suspected to be closed", e);
                }
            } catch (IOException e) {
                LOGGER.error("Error while establishing a connection2", e);
            }
    }


    /**
     * 
     * @see logmatcher.Collector#start()
     */
    public void start() throws IOException {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Starting collector, instance of " + ServerSocketLogCollector.class);
        }
        
        stopped = false;
        worker.start();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Collector started");
        }
    }


    /**
     * 
     * @see logmatcher.Collector#stop()
     */
    public void stop() throws IOException {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Stopping collector, instance of " + ServerSocketLogCollector.class);
        }

        stopped = true;
        socket1.close();
        socket2.close();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Collector stopped");
        }
    }


    /**
     * Read the object properties from configuration source.
     *
     * @see logmatcher.Configurable#configure(org.apache.commons.configuration.Configuration, java.lang.String)
     */
    public void configure(Configuration config, String id)
        throws InitializationException {
        Utils.autoConfigure(this, config, id);

        this.config = config;
    }


    /**
     * Get port on which server waits for incoming messages.
     *
     * @return int
     */
    public int getPortNumber() {
        return portNumber;
    }


    /**
     * Set port on which collector awaits connections.	
     *
     * @param portNumber Port number to be set.
     *
     * @throws IllegalStateException if collector is already running.
     */
    public void setPortNumber(int portNumber) {
        if (stopped) {
            this.portNumber = portNumber;
        } else {
            throw new IllegalStateException("Port number may not be changed while collector is running.");
        }
    }

    /**
     * This implementation do not support multiple listeners. Next call of this method overwrites previous
     * assignment.
     *
     * @see logmatcher.LogEventProducer#addLogEventListener(logmatcher.LogEventListener)
     */
    public void addLogEventListener(LogEventListener listener) {
        this.logEventListener = listener;
    }


    /**
     * 
     * @see logmatcher.LogEventProducer#addLogEventListener(logmatcher.LogEventListener, logmatcher.InputFilter)
     */
    public void addLogEventListener(LogEventListener listener, InputFilter filter) {
        throw new UnsupportedOperationException("This implementation do not support conditional subscriptions");
    }

	public String getHostName() {
		return hostName;
	}

	public void setHostName(String hostName) {
		this.hostName = hostName;
	}
    
    
}
